/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.zy.je.book.module.datasource.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.creator.druid.DruidConfig;
import com.zy.je.book.module.datasource.entity.GenDatasourceConf;
import com.zy.je.book.module.datasource.repository.GenDatasourceConfMapper;
import com.zy.je.book.module.datasource.service.GenDatasourceConfService;
import com.zy.je.common.core.exception.BizException;
import com.zy.je.common.core.utils.CheckUtil;
import com.zy.je.common.core.utils.SpringContextHolder;
import com.zy.je.common.core.utils.collect.ListUtil;
import com.zy.je.common.mybatis.ext.service.impl.BizServiceImpl;
import com.zy.jec.db.enums.DsConfTypeEnum;
import com.zy.jec.db.enums.DsJdbcUrlEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 数据源表
 *
 * @author lengleng
 * @date 2019-03-31 16:00:20
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GenDatasourceConfServiceImpl extends BizServiceImpl<GenDatasourceConfMapper, GenDatasourceConf>
        implements GenDatasourceConfService {

    private final StringEncryptor stringEncryptor;

    private final DefaultDataSourceCreator druidDataSourceCreator;

    @Override
    public List<String> bizBatchSave(List<GenDatasourceConf> dataList) {
        List<String> retList = ListUtil.newArrayList();
        List<GenDatasourceConf> updateList = dataList.stream()
                .filter(it -> !CheckUtil.isBlank(it.getId()))
                .collect(Collectors.toList());
        retList.addAll(batchUpdate(updateList));

        List<GenDatasourceConf> insertList = dataList.stream()
                .filter(it -> CheckUtil.isBlank(it.getId()))
                .collect(Collectors.toList());
        retList.addAll(batchInsert(insertList));
        return retList;
    }

    private Collection<String> batchInsert(List<GenDatasourceConf> insertList) {
        if(CheckUtil.isBlank(insertList)){
            return ListUtil.newArrayList();
        }
        // 校验配置合法性
        insertList.forEach(this::checkDataSource);

        DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
        // 添加
        insertList.forEach(this::addDynamicDataSource);

        // 更新数据库配置
        insertList.forEach(it -> {
            if (StrUtil.isNotBlank(it.getPassword())) {
                it.setPassword(stringEncryptor.encrypt(it.getPassword()));
            }
        });
        return super.bizBatchSave(insertList);
    }

    private List<String> batchUpdate(List<GenDatasourceConf> updateList) {
        if(CheckUtil.isBlank(updateList)){
            return ListUtil.newArrayList();
        }
        final CopyOptions copyOptions = CopyOptions.create();
        copyOptions.setOverride(false);
        copyOptions.setIgnoreNullValue(true);

        Map<Long, GenDatasourceConf> dbDataMap = ListUtil.toMap(
                super.listByIds(ListUtil.toSet(updateList, GenDatasourceConf::getId))
                , GenDatasourceConf::getId);

        List<GenDatasourceConf> dataList = ListUtil.newArrayList();
        updateList.forEach(it -> {
            GenDatasourceConf dest = new GenDatasourceConf();
            GenDatasourceConf src1 = dbDataMap.get(it.getId());
            if(!CheckUtil.isBlank(src1)){
                src1.setPassword(stringEncryptor.decrypt(src1.getPassword()));
                BeanUtil.copyProperties(src1, dest, copyOptions);
            }
            BeanUtil.copyProperties(it, dest, copyOptions);

            dataList.add(dest);
        });

        // 校验配置合法性
        dataList.forEach(this::checkDataSource);

        DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
        dataList.forEach(it -> {
            // 先移除
            dynamicRoutingDataSource.removeDataSource(it.getName());
            // 再添加
            addDynamicDataSource(it);
        });

        // 更新数据库配置
        dataList.forEach(it -> {
            if (StrUtil.isNotBlank(it.getPassword())) {
                it.setPassword(stringEncryptor.encrypt(it.getPassword()));
            }
        });
        return super.bizBatchSave(dataList);
    }

    @Override
    public List<String> bizBatchDelete(String id) {
        DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
        super.bizBatchGet(id)
                .forEach(ds -> dynamicRoutingDataSource.removeDataSource(ds.getName()));
        return super.bizBatchDelete(id);
    }

    /**
     * 添加动态数据源
     *
     * @param conf 数据源信息
     */
    @Override
    public void addDynamicDataSource(GenDatasourceConf conf) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        dataSourceProperty.setPoolName(conf.getName());
        dataSourceProperty.setUrl(conf.getUrl());
        dataSourceProperty.setUsername(conf.getUsername());
        dataSourceProperty.setPassword(conf.getPassword());

        // 增加 ValidationQuery 参数
        DruidConfig druidConfig = new DruidConfig();
        dataSourceProperty.setDruid(druidConfig);
        DataSource dataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);

        DynamicRoutingDataSource dynamicRoutingDataSource = SpringContextHolder.getBean(DynamicRoutingDataSource.class);
        dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource);
    }

    /**
     * 校验数据源配置是否有效
     *
     * @param conf 数据源信息
     * @return 有效/无效
     */
    @Override
    public void checkDataSource(GenDatasourceConf conf) {
        String url;
        // JDBC 配置形式
        if (DsConfTypeEnum.JDBC.getType().equals(conf.getConfType())) {
            url = conf.getUrl();
        } else if (DsJdbcUrlEnum.MSSQL.getDbName().equals(conf.getDsType())) {
            // 主机形式 sql server 特殊处理
            DsJdbcUrlEnum urlEnum = DsJdbcUrlEnum.get(conf.getDsType());
            url = String.format(urlEnum.getUrl(), conf.getHost(), conf.getPort(), conf.getDsName());
        } else {
            DsJdbcUrlEnum urlEnum = DsJdbcUrlEnum.get(conf.getDsType());
            url = String.format(urlEnum.getUrl(), conf.getHost(), conf.getPort(), conf.getDsName());
        }

        conf.setUrl(url);

        try (Connection connection = DriverManager.getConnection(url, conf.getUsername(), conf.getPassword())) {
        } catch (SQLException e) {
            log.error("数据源配置 {} , 获取链接失败", conf.getName(), e);
            throw new BizException("数据库配置错误，链接失败");
        }
    }

}
